/**
 * This is a testing framework. Use it extensively to verify that your code is working
 * properly.
 */
public class Tester {

	private static boolean testPassed = true;
	private static int testNum = 0;
	
	/**
	 * This entry function will test all classes created in this assignment.
	 * @param args command line arguments
	 */
	public static void main(String[] args) {
		
		// Each function here should test a different class.
		testPoint();
		testTriangle();
		testPolygon();
		testQuadrangle();
		testContainer();
		
		// Notifying the user that the code have passed all tests. 
		if (testPassed) {
			System.out.println("All " + testNum + " tests passed!");
		}
	}

	/**
	 * This utility function will count the number of times it was invoked. 
	 * In addition, if a test fails the function will print the error message.  
	 * @param exp The actual test condition
	 * @param msg An error message, will be printed to the screen in case the test fails.
	 */
	private static void test(boolean exp, String msg) {
		testNum++;
		
		if (!exp) {
			testPassed = false;
			System.out.println("Test " + testNum + " failed: "  + msg);
		}
	}
	
	/**
	 * Checks the Point class.
	 */
	private static void testPoint() {
		
		Point p1 = new Point(10, 20);
		test(p1.getX() == 10, "X should be 10.");
		test(p1.getY() == 20, "Y should be 20.");
		Point p2 = new Point(p1);
		test(p2.equals(p1), "Point should be equal.");
		test(p2 != p1, "Point should be different points.");
		test(p2.distance(p1) == 0, "Distance should be zero.");
		p2.move(p1);
		test(p2.getX() == 2*p1.getX(), "P2 x should be twice as P1 x.");
		test(p2.getY() == 2*p1.getY(), "P2 y should be twice as P1 y.");
		test(p2.distance(p1) == 22.360679774997898, "Distance should be 22.360679774997898");
		test(!p2.equals(p1), "Point should not be equal.");
		p2.move(1,1);
		test(p2.getX() == 2*p1.getX()+1, "P2 x should be twice as P1 x + 1.");
		test(p2.getY() == 2*p1.getY()+1, "P2 y should be twice as P1 y + 1.");
		test(p2.distance(p1) == 23.706539182259394, "Distance should be 23.706539182259394");
		boolean test=false;
		try {
			Point p3 = new Point(null);
		} catch (Exception e) {
			test=true;
			//StackTraceElement[] trace =e.getStackTrace();
			//System.out.println(trace[2].toString());
			//e.printStackTrace();
		}
		test(test,"no error on null input to point constructor");
		
	}

	/**
	 * Checks the Triangle class.
	 */
	private static void testTriangle() {
		Point p1 = new Point(0,0);
		Point p2 = new Point(0,3);
		Point p3 = new Point(4,0);
		Triangle t1 = new Triangle (p1,p2,p3);
		Triangle t2 = new Triangle (t1);
		test(t1!=t2,"test tri.1 - objects should not be equal");
		test(t1.getArea()==6,"test tri.2 area incorrect,should be 6 got:"+t1.getArea());
		test(t1.getPerimeter()==12,"test tri.3 perimeter incorrect");
		test(t1.equals(t2),"test tri.4 triangles not logicly equal");
		test(t1.getP1().distance(t1.getP2()) == 3 && t1.getP1().distance(t1.getP3())==4 && t1.getP2().distance(t1.getP3())==5,"test tr.5 points length issue");
		p1.move(1,1);
		test(t1.getArea()==6,"test tri.5 triangle corner moved after object initialization");
		test(t1.contains(p1),"test tri.6 point should be inside triangle");
		p1.move(-3,-2);
		test(!t1.contains(p1),"test tri.7 point should be outside triangle");
		test(!(t1.getP1()==p1 || t1.getP2()==p2 || t1.getP3()==p3),"test tri.8 pointer to original point given");
		t1.getP1().move(1,1);
		test(t1.getArea()==6,"test tri.9 triangle corner moved after object initialization");
		t2.move(p1);
		test(t2.getP1().getX()==-2 &&t2.getP1().getY()==-1, "points not moved after calling move");
		test(t1.getArea()==6,"test tri.10 triangle corner moved after object initialization");
		test(!t1.equals(t2),"test tri.11 triangles should not be equal after move");
		p1= new Point(0,0);
		t2= new Triangle(p3,p2,p1);
		test(t1.equals(t2),"test tri.12 triangles should be equal with different order");
		
	}
	
	private static void testContainer()
	{
		boolean test=false;
		try
		{
			ShapesContainer sh1 = new ShapesContainer(null);
		}
		catch(RuntimeException e) {
			test=true;
		}
		test(test,"test container.1 no exception on null input to constructor");
		ShapesContainer sh1 = new ShapesContainer();
		test(sh1.getShapesNum()==0,"test container.2 array initial shapes not empty");
		test(sh1.add(new Triangle(new Point(0,0),new Point(3,0),new Point(0,4))),"test container.3 failed to add shape");
		sh1.add(new Triangle(new Point(0,0),new Point(3,0),new Point(0,40)));
		test(sh1.getShapesNum()==2,"test container.3 shapes num not increased after adding shapes");
		test(sh1.getShape(0).getArea()>sh1.getShape(1).getArea(),"test container.4 shapes not sorted according to size");
		ShapesContainer sh2 = new ShapesContainer(sh1);
		test(!(sh1==sh2),"test container.4 containers have same address");
		test(sh2.getShapesNum()==2,"test container.5 shapes not copied by copy constructor");
		Triangle t1 = new Triangle(new Point(0,0),new Point(3,0),new Point(0,400));
		test(sh1.add(t1),"test container.6 failed to add logicly equal shape");
		test(!sh1.add(t1),"test container.7 added same shape twice");
		test(sh1.remove(t1),"test container.8 failed to remove shape");
		test(!sh1.remove(t1),"test container.9 shape not actualy removed");
		int sNum=sh1.getShapesNum();
		test(sh1.remove(0) && sNum-1==sh1.getShapesNum(),"test container.10 shape not actualy removed");
		test=false;
		try
		{
			sh1.remove(sh1.getShapesNum());
		}
			catch(RuntimeException e) {
				test=true;
			}
		test(test,"test container.11 error not thrown on invalid removal index");
		sh2 = new ShapesContainer();
		sh2.add(new Triangle(new Point(0,0),new Point(3,0),new Point(0,4)));
		sh2.add(new Quadrangle(new Point(0,0),new Point(3,0),new Point(0,3),new Point(3,3)));
		sh2.add(new Circle(new Point(0,0),1));
		double sum = new Circle(new Point(0,0),1).getArea() + new Quadrangle(new Point(0,0),new Point(3,0),new Point(0,3),new Point(3,3)).getArea()+new Triangle(new Point(0,0),new Point(3,0),new Point(0,4)).getArea();
		test(sh2.sumArea()==sum,"test container.12 sumArea incorrect result " + sum + " " + sh2.sumArea());
		sum = new Circle(new Point(0,0),1).getPerimeter() + new Quadrangle(new Point(0,0),new Point(3,0),new Point(0,3),new Point(3,3)).getPerimeter()+new Triangle(new Point(0,0),new Point(3,0),new Point(0,4)).getPerimeter();
		test(sh2.sumPerimeter()==sum,"test container.13 sumPerimieter incorrect result " + sum + " " + sh2.sumPerimeter());
		test=false;
		try
		{
			Shape t2 = sh1.getShape(9);
		}
			catch(RuntimeException e) {
				test=true;
			}
		test(test,"test container.12 error not thrown on invalid getShape index");
		//Shape t2 = sh1.getShape(0);
		//Quadrangle t3 = (Quadrangle)t2;
		test=false;
		try
		{
			Shape t2 = sh1.getShape(-1);
		}
			catch(RuntimeException e) {
				test=true;
			}
		test(test,"test container.13 error not thrown on invalid getShape index");
		test=false;
		try
		{
			boolean stam = sh1.remove(-1);
		}
			catch(RuntimeException e) {
				test=true;
			}
		test(test,"test container.14 error not thrown on invalid remove index");
		
	}
	private static void testPolygon() {
		Point p1 = new Point(0,0);
		Point p2 = new Point(1,1);
		Point p3 = new Point(2,-3);
		Point p4 = new Point(-1,-2);
		Point p8 = new Point(-1,-1);
		Point p9 = new Point(-2,-3);
		Point p10 = new Point(-5,-3);
		Point p11 = new Point(-4,-1);
		Polygon t = new Quadrangle(p1,p2,p3,p4);
		//Polygon p = new Triangle();
		Polygon t2 = new Triangle(p1,p2,p3);
		boolean test = false;
		try{
		Polygon t3 = new Triangle(p1,p2,null);
		Polygon t4 = null;
		} catch(RuntimeException e) {
			test=true;
		}
		test(test,"test poly.4 no exception on null point for constructor");
		double[] x;
		Polygon t4 = new Quadrangle(p8,p9,p10,p11);
//		test 2 - getNumofPoints
		test(t4.getNumOfPoints()==4,"test Polygon ");
//  	test 3 - getSides
		x=t4.getSides();
		test(Math.sqrt(5.0)>=x[0]-0.0001 && Math.sqrt(5.0)<=x[0]+0.0001 && x[1]==3 && x[3]==3 && Math.sqrt(5.0)>=x[2]-0.0001 && Math.sqrt(5.0)<=x[2]+0.0001, "test 3 Polygon for getSides failed");
		
//		tests for getPerimeter,equals, is in Triangle
		
	}

	
	
	private static void testQuadrangle() {
		Point p1 = new Point(0,0);
		Point p2 = new Point(1,1);
		Point p3 = new Point(2,-3);
		Point p4 = new Point(-1,-2);
		Point p5 = new Point(0,4);
		Point p6 = new Point(3,4);
		Point p7 = new Point(3,0);
		Point p8 = new Point(-1,-1);
		Point p9 = new Point(-2,-3);
		Point p10 = new Point(-5,-3);
		Point p11 = new Point(-4,-1);
		Point testP = null;
		double x,y;
//		test 1 - constructor 1
		try{
		Quadrangle t1 = new Quadrangle(p1,p2,p3,p4);	// (0,0) , (1,1) , (2,-3) , (-1,-2)
		}catch(RuntimeException e) {
			test(false,"test 1 for Quadrangle constructor failed - Caught Exception:"+e.getMessage());
		}
		Quadrangle t1 = new Quadrangle(p1,p2,p3,p4);	// (0,0) , (1,1) , (2,-3) , (-1,-2)  - some meruba
		Quadrangle t2 = new Quadrangle(p1,p5,p6,p7);    // (0,0) , (0,4) , (3,4) , (3,0)  - malben
		Quadrangle t31 = new Quadrangle(p8,p9,p10,p11);	// (-1,-1) , (-2,-3) , (-5,-3) , (-4,-1) - makbilit
		Quadrangle t32 = new Quadrangle(p11,p8,p9,p10);	// (-1,-1) , (-2,-3) , (-5,-3) , (-4,-1) - same makbilit different order
		Quadrangle t33 = new Quadrangle(p8,p11,p10,p9);	// (-1,-1) , (-2,-3) , (-5,-3) , (-4,-1) - same makbilit different order
//		test 2 - constructor 2
		try{
		Quadrangle t5 = new Quadrangle(t2);
		}catch(RuntimeException e) {
			test(false,"test 2 for Quadrangle constructor failed - Caught Exception:"+e.getMessage());
		}
		test(t1.getP1().getX()==0 && t1.getP1().getY() == 0 && t1.getP2().getX()==1 && t1.getP2().getY()==1 && t1.getP3().getX()==2 && t1.getP3().getY() == -3 && t1.getP4().getX()==-1 && t1.getP4().getY() == -2 ,"test 3/4/5/6 for Quadrangle getPoints");
// 		test 7 - getArea 31
		x = t31.getArea();
		test(6>=x-0.0001 && 6<=x+0.0001,"test 7 for Quadrangle getArea 31 - Expected: 6 , REAL:" + x);
// 		test 8 - getArea 32
		x = t32.getArea();
		test(6>=x-0.0001 && 6<=x+0.0001,"test 8 for Quadrangle getArea 32 - Expected: 6 , REAL:" + x);
// 		test 9 - getArea 33
		x = t33.getArea();
		test(6>=x-0.0001 && 6<=x+0.0001,"test 9 for Quadrangle getArea 33 - Expected: 6 , REAL:" + x);
///////////////////////////		
		boolean ans;
		boolean test =false;
	//test 10  - contains		
		try{
			ans = t1.contains(null);
		} catch(RuntimeException e) {
			test = true;
		} 
		test(test,"test 10 failed - didnt catch RuntimeException for object null");
			
// 		test 11 - contains right side (r traingle)
		ans = t31.contains(new Point(-2,-2));
		test(ans,"test 11 for Quadrangle contains - Expected: true , REAL:" + ans );
// 		test 12 - contains dot in middle
		ans = t31.contains(new Point(-3,-2));
		test(ans,"test 12 for Quadrangle contains dot in middle - Expected: true , REAL:" + ans );
//		test 13 - contains dot on alachson - y=0.5x-0.5
		ans = t31.contains(new Point(-2,-1.5));
		test(ans,"test 13 for Quadrangle contains dot on alachson - Expected: true , REAL:" + ans );
// 		test 14 - contains left side (l traingle)
		ans = t31.contains(new Point(-4,-2.85));
		test(ans,"test 14 for Quadrangle contains - Expected: true , REAL:" + ans );		
// 		test 15 - contains kodkod)
		ans = t31.contains(p8);
		test(ans,"test 15 for Quadrangle contains kodkod - Expected: true , REAL:" + ans );
// 		test 16 - contains for same makbilit
		ans = t32.contains(new Point(-2,-2));
		test(ans,"test 16 for Quadrangle contains - Expected: true , REAL:" + ans );
// 		test 17 - contains - not contains
		ans = t1.contains(new Point(0,0.1));
		test(!ans,"test 17 for Quadrangle contains not contains - Expected: false , REAL:" + ans );
		
// 		test 18 - equals traingle		
		Triangle tt= new Triangle(p8,p9,p10);
		ans = t31.equals(tt);
		test(!ans,"test 18 for Quadrangle equals traingle - Expected: false , REAL:" + ans );

// 		test 19 - equals true
		ans = t31.equals(t32);
		test(ans,"test 19 for Quadrangle equals - Expected: true , REAL:" + ans );
// 		test 20 - equals true
		test(t31.equals(t33),"test 20 for Quadrangle equals failed");
		//System.out.println("test 20 for Quadrangle equals - Expected: true , REAL:" + ans );
		
// 		test 21 - equals false
		
		//System.out.println("test 21 for Quadrangle equals - Expected: false , REAL:" + ans );
		test(!t31.equals(t1),"test 21 for Quadrangle equals failed");
//		test 22 - move dont move	
		t31.move(p1);			// point 0,0 not suppose to move the quad
			// t31 and t32 are equal but not the same object. if not equal - it was moved
		test(t31.equals(t32),"test 22 for Quadrangle move , dont move failed");
//		test 23 - move	
		t33.move(p2);			// point p2 suppose to move the quad
		test(!t33.equals(t32),"test 23 for Quadrangle move failed");		// t33 and t32 are equal but not the same object. if not equal - it was moved
		// pay attention - t33 no longer = t31 and t32!
//		test 24 - move
		Point p12 = new Point(0.4,-1.5);
		Point p22 = new Point(1.4,-0.5);
		Point p32 = new Point(2.4,-4.5);
		Point p42 = new Point(-0.6,-3.5);
		Quadrangle t12 = new Quadrangle(p12,p22,p32,p42);	
		t1.move(p12);		//(1,1) , (2,-3) , (-1,-2)
		test(t1.equals(t12),"test 24 for Quadrangle move failed");
			

	}

}